Skip to content

Feat/integrate context management#119

Open
rtb-12 wants to merge 31 commits intomasterfrom
feat/integrate-context-management
Open

Feat/integrate context management#119
rtb-12 wants to merge 31 commits intomasterfrom
feat/integrate-context-management

Conversation

@rtb-12
Copy link
Copy Markdown
Contributor

@rtb-12 rtb-12 commented Mar 11, 2026

[mero-chat] Integrate context group management across app logic and frontend

Description

Migrates mero-chat from its legacy single-context-with-channels architecture to the group-based context management model, where each channel and DM is its own context within a group. This is the full adoption of the context group management feature implemented in core (feat/context-management-proposal).

Motivation: The previous architecture used one shared context for all channels and a separate context per DM with a bespoke state machine. The new model aligns with core's group primitives — visibility, allowlists, capabilities, cascade removal, and upgrade propagation — giving admins proper control over workspace membership and per-channel access.

What changed :

  • App logic (Rust): Stripped channel/DM management from logic/src/lib.rs; each context now holds only messages, reactions, and per-context metadata (get_info, set_profile, get_profiles). The schema is single-context: one context = one channel or one DM.
  • Group API layer: Added groupApiDataSource.ts with full coverage of the admin-api group surface (create/get/list/delete group, invite, join, members, contexts, visibility, allowlist, capabilities, default settings, upgrade trigger/status). Added useGroup, useGroupContexts, useGroupMembers hooks and groupId session/URL config.
  • Onboarding: Workspace creation calls createGroup → first context in group → createInvitation. Joining uses joinGroupsyncGrouplistGroupContextsjoinGroupContext. InvitationHandlerPopup detects group vs legacy invitations. CreateWorkspacePopup added.
  • Channels: Channel list sourced from useGroupContexts + per-context get_info(). Create channel = createGroupContext (with optional restricted visibility). Switch channel = switch contextId + context identity. SearchChannelsContainer updated for browse-and-join flow.
  • DMs: Create DM = context in group + setContextVisibility("restricted") + addToAllowlist([self, other]). DM list filtered from group contexts by context_type === "Dm". Removed dmSetupState.ts and legacy DM invitation flow entirely.
  • Admin UI: New admin panel (shield icon in navbar) with four tabs — Members (list, remove with cascade confirmation, per-member capabilities), Channels (per-context visibility toggle, allowlist add/remove), Settings (group info, default visibility/capabilities), Upgrade (trigger, progress bar with auto-poll).
  • Cleanup: Removed HandleDMSetup, HandleInvitation, InvitationPending, JoinContext, SyncWaiting, dmSetupState.ts, and legacy session helpers. Simplified useChatHandlers and ChatContainer.

Dependencies: Requires core built from feat/context-management-proposal with group endpoints available at /admin-api/groups/*.

Test plan

  1. Prerequisites: Two local merod nodes (node-a, node-b) with the context-groups contract deployed and core built from feat/context-management-proposal.
  2. Workspace creation : Open mero-chat pointed at node-a. Create a workspace — verify group is created, #general context exists, and an invite link is generated.
  3. Join via invitation : Open a second browser/incognito pointed at node-b. Paste the invite link — verify joinGroupsyncGroup → auto-join #general succeeds and messages can be sent/received.
  4. Channel management : Create an open channel and a restricted channel from node-a. From node-b, browse channels and join the open one. Verify the restricted channel is not joinable without allowlist entry. Add node-b to the restricted channel's allowlist via admin panel, then join — verify messages work.
  5. DM flow : From node-a, start a DM with node-b's identity. Verify a restricted context is created with both identities on the allowlist. Node-b should see the DM in their sidebar and be able to send messages.
  6. Admin panel :
    • Members tab: Verify member list loads. Remove a non-admin member — confirm cascade removal (member loses access to all group contexts, gets errors on next action).
    • Channels tab: Toggle a channel between open/restricted. Add/remove identities from the allowlist.
    • Settings tab: Change default visibility and default capabilities.
    • Upgrade tab: Trigger an upgrade with a new application ID. Verify progress polling and status display.
  7. TypeScript: npx tsc --noEmit passes with zero errors.

E2E test candidates: group join via invite, create channel, join channel, send message, create DM, join DM, send message, admin remove member (verify access revoked).

Documentation update

  • The admin panel is self-explanatory in the UI; no separate user-facing docs exist for mero-chat today. If user docs are added, they should cover: workspace creation, invitation flow, channel/DM management, and the admin panel (members, visibility, allowlist, upgrades).

Note

Medium Risk
Adds new CI workflows that spin up real nodes and run extensive Playwright/RPC test suites, which can introduce flakiness and longer CI times. Also changes dependency/automation config (Dependabot, Makefiles) that affects developer and CI tooling paths.

Overview
Expands automated testing and CI for the frontend and full stack. Adds a new Frontend CI GitHub Action to typecheck, lint, run unit tests (Vitest), and run Playwright E2E tests for app/** changes.

Introduces full-stack integration CI. Adds an Integration Tests (Full Stack) workflow that builds curb.wasm, boots two real nodes via merobox, generates app/.env.integration with JWTs/IDs, and runs Playwright integration tests; workflow tests are also updated to install merobox via pip and to run e2e.yml + integration-setup.yml.

Adds a Playwright-based test harness and developer tooling. Introduces app/playwright.config.ts, e2e/global-setup.ts, new mocked/live/integration/rpc/rpc-admin specs and helpers, plus Makefile targets and npm scripts for running suites locally; updates .gitignore to exclude token/auth artifacts and tweaks ESLint ignores to include dev-dist.

Reviewed by Cursor Bugbot for commit a01e328. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
calimero-curb-chat Ready Ready Preview, Comment Apr 25, 2026 8:44pm
calimero-curb-rs Ready Ready Preview, Comment Apr 25, 2026 8:44pm

Request Review

Comment thread app/src/components/admin/ChannelsTab.tsx
Comment thread app/src/components/searchChannels/SearchChannelsContainer.tsx
Comment thread app/src/components/searchChannels/SearchChannelsContainer.tsx
Comment thread app/src/pages/Home/index.tsx Outdated
Comment thread app/src/pages/Home/index.tsx
Comment thread app/src/chat/ChatContainer.tsx
Comment thread app/src/chat/ChatContainer.tsx
Comment thread app/src/api/dataSource/groupApiDataSource.ts Outdated
Comment thread app/src/components/admin/AdminPanel.tsx Outdated
Comment thread app/src/App.tsx
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

There are 4 total unresolved issues (including 1 from previous review).

Autofix Details

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Infinite re-fetch loop from unstable admin dependency
    • Changed useEffect dependency from unstable admin object to stable admin.fetchAll function reference.
  • ✅ Fixed: Missing data field in getUsername return type
    • Added missing data: null field to catch block return statement to match ApiResponse contract.
  • ✅ Fixed: handleRefresh has unstable admin dependency causing re-creation
    • Changed useCallback dependency from unstable admin object to stable admin.fetchAll function reference.

Create PR

Or push these changes by commenting:

@cursor push 244ffd938b
Preview (244ffd938b)
diff --git a/app/src/api/dataSource/clientApiDataSource.ts b/app/src/api/dataSource/clientApiDataSource.ts
--- a/app/src/api/dataSource/clientApiDataSource.ts
+++ b/app/src/api/dataSource/clientApiDataSource.ts
@@ -1514,6 +1514,7 @@
         errorMessage = error;
       }
       return {
+        data: null,
         error: {
           code: 500,
           message: errorMessage,

diff --git a/app/src/components/admin/AdminPanel.tsx b/app/src/components/admin/AdminPanel.tsx
--- a/app/src/components/admin/AdminPanel.tsx
+++ b/app/src/components/admin/AdminPanel.tsx
@@ -170,11 +170,11 @@
     if (isOpen && groupId) {
       admin.fetchAll(groupId);
     }
-  }, [admin, groupId, isOpen]);
+  }, [admin.fetchAll, groupId, isOpen]);
 
   const handleRefresh = useCallback(() => {
     if (groupId) admin.fetchAll(groupId);
-  }, [groupId, admin]);
+  }, [groupId, admin.fetchAll]);
 
   if (!groupId || permissions.loading || !permissions.isAdmin) {
     return null;

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Comment thread app/src/components/admin/AdminPanel.tsx Outdated
Comment thread app/src/components/admin/AdminPanel.tsx Outdated
Comment thread app/src/api/dataSource/clientApiDataSource.ts
Comment thread app/src/chat/ChatDisplaySplit.tsx
Comment thread app/src/api/dataSource/clientApiDataSource.ts
const [actionLoading, setActionLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const api = new GroupApiDataSource();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New GroupApiDataSource created every render in hook

Low Severity

const api = new GroupApiDataSource() is called in the hook's function body, creating a new instance on every render. The useCallback functions that reference api all have empty dependency arrays, so they close over the first instance and ignore subsequent ones. The newly created objects are immediately discarded. Moving this to a module-level constant or useRef avoids the unnecessary allocations.

Fix in Cursor Fix in Web

Comment thread .github/workflows/integration-ci.yml
Comment thread app/package.json Outdated
Comment thread app/e2e/auth.spec.ts
Comment thread app/e2e/helpers/node-client.ts
rtb-12 and others added 24 commits April 25, 2026 18:20
Move app entry to a workspace-first flow that resolves the node's group identity before entering chat, and finish channel joins by carrying users into profile completion or chat based on their joined context state.

Made-with: Cursor
Use one global messenger name across the app, keep workspace invitation entry available from the welcome screen, and align group channel visibility/admin access with the current workspace settings and permissions.

Made-with: Cursor
Make workspace administration easier by replacing raw capability bitmasks with permission toggles and by letting authenticated users switch workspaces without redoing node auth. This also improves zero-channel workspace UX with clearer empty states and focused test coverage.

Made-with: Cursor
Carry richer group context metadata through the app so channel and DM flows can use shared context information instead of raw IDs alone. This also improves DM creation and discovery across the workspace and preserves the accompanying test coverage.

Made-with: Cursor
Expose group and member aliases through the frontend data layer so workspace, DM, and member-management surfaces show friendly names without replacing identity-based behavior. Add focused coverage for workspace alias rendering and DM/member alias fallbacks.

Made-with: Cursor
Resolve the current workspace member when a workspace is selected so the login name field reflects that member's alias, and persist the typed name back as the member alias before entering chat.

Made-with: Cursor
Implement support for group aliases throughout the application, allowing users to create and join groups with associated aliases. Update the invitation handling to serialize and parse group invitations with aliases, ensuring that aliases are stored and retrieved correctly. This includes updates to the API data source, UI components, and tests to ensure proper functionality and coverage.

Made-with: Cursor
(.data // .) |
if type == "array" then .[0].groupId
elif type == "object" then (.groups[0].groupId // .items[0].groupId)
else empty end' 2>/dev/null || echo "")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI group discovery queries wrong endpoint and field

High Severity

The CI bootstrap script queries /admin-api/groups and extracts groupId from the response, but the actual node API list endpoint is /admin-api/namespaces and returns entries with a namespaceId field. The frontend's listGroups() in groupApiDataSource.ts confirms this by calling /admin-api/namespaces as the primary endpoint, and rpc-admin.spec.ts tests explicitly state "The node exposes namespaces under /namespaces (not /groups)." The integration-setup.yml workflow also uses create_namespace and outputs namespaceId. As a result, GROUP_ID is always empty, E2E_GROUP_ID is blank in .env.integration, integrationEnvAvailable() returns false, and every integration test silently skips — despite the expensive WASM build, node startup, and Playwright browser installation.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3d7b388. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a01e328. Configure here.

Comment thread app/e2e/rpc-admin.spec.ts
if (!envAvailable()) {
test.skip();
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Admin tests skip unnecessarily due to strict env check

Low Severity

requireEnv() calls envAvailable() which requires memberKey, but none of the admin REST API tests actually need memberKey — the only reference on line 158 is already guarded with if (env.memberKey && ...). When E2E_MEMBER_KEY is absent but all other env vars are present, the entire admin test suite silently skips. A less strict check (like integrationEnvAvailable() from node-client.ts, which omits memberKey) would match the actual requirements.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a01e328. Configure here.

Comment thread app/e2e/global-setup.ts
const skipReasons = [
process.env.SKIP_DEV_SERVER && "SKIP_DEV_SERVER",
process.env.INTEGRATION_MODE && "INTEGRATION_MODE",
].filter(Boolean);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Global setup ignores LIVE_AUTH despite comment requiring it

Medium Severity

The comment on line 74 states "Only attempt auth when LIVE_AUTH=1 is explicitly set," and the Makefile passes LIVE_AUTH=1 for the live project, but process.env.LIVE_AUTH is never read in the skipReasons array or anywhere else. When a stale .env.integration exists with E2E_NODE_URL set but an expired token, running mocked tests (which don't need auth) causes global-setup to fall through to the browser auth flow, which fails against a non-running node and blocks the entire test suite.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a01e328. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants